﻿/*
COPYRIGHT (C) 2009 EPISERVER AB

THIS FILE IS PART OF REMOTE PAGE PROVIDER FOR EPISERVER CMS.
REMOTE PAGE PROVIDER FOR EPISERVER CMS IS FREE SOFTWARE: YOU CAN REDISTRIBUTE IT AND/OR MODIFY IT UNDER THE TERMS OF THE GNU LESSER GENERAL PUBLIC LICENSE VERSION v2.1 AS PUBLISHED BY THE FREE SOFTWARE FOUNDATION.

REMOTE PAGE PROVIDER FOR EPISERVER CMS IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU LESSER GENERAL PUBLIC LICENSE FOR MORE DETAILS.
YOU SHOULD HAVE RECEIVED A COPY OF THE GNU LESSER GENERAL PUBLIC LICENSE ALONG WITH REMOTE PAGE PROVIDER FOR EPISERVER CMS]. IF NOT, SEE <HTTP://WWW.GNU.ORG/LICENSES/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EPiServer.Web.Hosting;
using System.IO;
using System.Web;
using System.Diagnostics;
using System.ServiceModel;

namespace EPiServer.Labs.RemoteProvider
{
    /// <summary>
    /// This class presents a collection of Remote VPPs and it contains All remote Vpps info.
    /// It helps to find the appropriate Remote Vpp. 
    /// </summary>
    public class RemoteVirtualPathProviderAggregate
    {

        /// <summary>
        /// The root Inforamtion This is a First Level unifiedDirectory 
        /// </summary>
        RemoteUnifiedDirectory                          _root;

        RemoteVirtualPathProvider                       _provider;

        //Key is VirtualPath and Value is the Remote VirtualPathProvider presentation as RemoteUnifedDirectory
        Dictionary<string, RemoteUnifiedDirectory>      _remoteVirtualDirectories;
        Dictionary<string, RemoteUnifiedDirectory>      _remoteVisisbleVirtualDirectories;
        
        object                                          _syncObject = new object();
        RemoteUnifiedDirectory                          _remotePageFolder;
        string                                          _remotePageFolderVirtualPath;

        //Key is VirtualPath abd Value is the Remote VirtualPathProvider Info
        Dictionary<string, RemoteVirtualPathProviderInfo>      _remoteVirtualPathProvidersInfo;

        

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="RemoteVirtualPathProviderAggregate"/> class.
        /// </summary>
        /// <param name="remoteFileServer">The remote file server.</param>
        /// <param name="provider">The provider.</param>
        public RemoteVirtualPathProviderAggregate(
            RemoteVirtualPathProvider provider)
        {
            _provider = provider;
        }
        #endregion

        #region Property
        /// <summary>
        /// Gets the root. This is the root directory or main VPP for all other remote VPPS
        /// </summary>
        /// <value>The root.</value>
        public RemoteUnifiedDirectory Root
        {
            get
            {
                if (_root != null)
                {
                    return _root;
                }
                _root = MakeRootDir();
                return _root;
            }
        }

        /// <summary>
        /// Gets the remote virtual path providers as unifiedDirettory.
        /// </summary>
        /// <value>The remote virtual path providers.</value>
        public RemoteUnifiedDirectory[] RemoteVirtualDirectories
        {

            get
            {
                if (_remoteVisisbleVirtualDirectories != null)
                {
                    return _remoteVisisbleVirtualDirectories.Values.ToArray();
                }
                CreateRemoteVirtualDirectories();
                return _remoteVisisbleVirtualDirectories != null ? _remoteVisisbleVirtualDirectories.Values.ToArray() : null;
            }
        }

        #endregion

        #region Helper Method
        /// <summary>
        /// Creates the remote virtual directories.
        /// </summary>
        private void CreateRemoteVirtualDirectories()
        {
            if (_provider.RemoteVirtualPathProviders == null)
            {
                return;
            }

            Int32 capacity = _provider.RemoteVirtualPathProviders.VirtualPathProviders.Count;
            _remoteVisisbleVirtualDirectories = new Dictionary<string, RemoteUnifiedDirectory>(capacity);
            _remoteVirtualDirectories = new Dictionary<string, RemoteUnifiedDirectory>(capacity);
            _remoteVirtualPathProvidersInfo = new Dictionary<string, RemoteVirtualPathProviderInfo>(capacity);

            foreach (RemoteVirtualPathProviderInfo remoteVirtualPathProviderInfo in _provider.RemoteVirtualPathProviders.VirtualPathProviders.Values)
            {

                // The VirtualProviderPahProvider is not unique
                if (!_remoteVirtualPathProvidersInfo.ContainsKey(remoteVirtualPathProviderInfo.VirtualPathRoot))
                {
                    _remoteVirtualPathProvidersInfo.Add(remoteVirtualPathProviderInfo.VirtualPathRoot, remoteVirtualPathProviderInfo);
                }

                if (_remotePageFolder == null && remoteVirtualPathProviderInfo.IsPageFolderVirtualPathProvider)
                {
                    _remotePageFolderVirtualPath = VirtualPathUtility.RemoveTrailingSlash(remoteVirtualPathProviderInfo.VirtualPathRoot.Substring(1));
                    _remotePageFolder = MakeDir(
                                                remoteVirtualPathProviderInfo.Name ,
                                                 _provider.VirtualPathRoot + remoteVirtualPathProviderInfo.VirtualPathRoot,
                                                Root.DirInfo,
                                                true,
                                                false,
                                                remoteVirtualPathProviderInfo.BypassAccessCheck); 
                        
                    continue;
                }

                if (_remoteVirtualDirectories.ContainsKey(remoteVirtualPathProviderInfo.VirtualPathRoot) )
                {
                    continue;
                }
                RemoteUnifiedDirectory root = MakeDir(
                                                remoteVirtualPathProviderInfo.Name,
                                                _provider.VirtualPathRoot + remoteVirtualPathProviderInfo.VirtualPathRoot,
                                                Root.DirInfo,
                                                true,
                                                false,
                                                remoteVirtualPathProviderInfo.BypassAccessCheck);
                
                //We will have access to all remote vpp
                _remoteVirtualDirectories.Add(remoteVirtualPathProviderInfo.VirtualPathRoot, root);
                
                
                //We show them which has showInRemoteContect attribute true or showInFileManager true
                if (remoteVirtualPathProviderInfo.ShowInFileManager)
                {
                    _remoteVisisbleVirtualDirectories.Add(remoteVirtualPathProviderInfo.VirtualPathRoot, root);
                }
            }
        }

        /// <summary>
        /// Makes the root dir.
        /// </summary>
        /// <returns></returns>
        private RemoteUnifiedDirectory MakeRootDir()
        {
            return MakeDir(_provider.ProviderName, _provider.VirtualPathRoot, null, true, true, _provider.BypassAccessCheck);
        }

        /// <summary>
        /// Makes the dir.
        /// </summary>
        /// <param name="providerName">Name of the provider.</param>
        /// <param name="virtualPath">The virtual path.</param>
        /// <param name="parent">The parent.</param>
        /// <param name="isDirectory">if set to <c>true</c> [is directory].</param>
        /// <param name="isFirstLevel">if set to <c>true</c> [is first level].</param>
        /// <param name="byPassAccessCheck">if set to <c>true</c> [by pass access check].</param>
        /// <returns></returns>
        private RemoteUnifiedDirectory MakeDir(
            string providerName,
            string virtualPath, 
            RemoteDirectoryInfo parent,
            bool isDirectory,
            bool isFirstLevel,
            bool byPassAccessCheck)
        {
            RemoteDirectoryInfo remoteRootInfo = new RemoteDirectoryInfo();
            remoteRootInfo.ProviderName = providerName;
            remoteRootInfo.VirtualPath = virtualPath;
            remoteRootInfo.Parent = parent;
            remoteRootInfo.IsDirectory = isDirectory;
            remoteRootInfo.IsFirstLevel = isFirstLevel;

            remoteRootInfo.BypassAccessCheck = byPassAccessCheck;

            return new RemoteUnifiedDirectory(_provider.RemoteFileProvider , _provider, remoteRootInfo);
        }

        #endregion

        #region public Method
        /// <summary>
        /// Tries the find to remote virtual path provider.
        /// </summary>
        /// <param name="virtualPath">The virtual path.</param>
        /// <param name="remoteUnifiedDirectory">The remote unified directory.</param>
        /// <param name="remoteVirtualPath">The remote virtual path.</param>
        /// <returns></returns>
        public bool TryToFindRemoteVirtualPath(
                string virtualPath, 
                out RemoteUnifiedDirectory remoteUnifiedDirectory, 
                out string remoteVirtualPath)
        {
            remoteUnifiedDirectory = null;
            remoteVirtualPath = null;

            // Quick check if the path is relevant here
            if (virtualPath.StartsWith(_provider.VirtualPathRoot))
            {
                if (virtualPath == _provider.VirtualPathRoot)
                {
                    remoteUnifiedDirectory = Root;
                    return true;
                }

                string[] pathInfo =  virtualPath.Split(new char[]{'/'},4);

                if (pathInfo != null && pathInfo.Length > 2)
                {

                    Int32 pageFolderId;
                    if (Int32.TryParse(pathInfo[2], out pageFolderId))
                    {
                        remoteUnifiedDirectory = _remotePageFolder;
                        remoteVirtualPath = "/" +_remotePageFolderVirtualPath + virtualPath.Substring(_provider.VirtualPathRoot.Length - 1);
                        return true;
                    }
                 
                    if (pathInfo[2].StartsWith(_remotePageFolderVirtualPath))
                    {
                        remoteUnifiedDirectory = _remotePageFolder;
                        remoteVirtualPath = virtualPath.Substring(_provider.VirtualPathRoot.Length - 1);
                        return true;
                    }

                    if (_remoteVirtualDirectories.TryGetValue("/" + pathInfo[2] + "/", out remoteUnifiedDirectory))
                    {
                        remoteVirtualPath = virtualPath.Substring(_provider.VirtualPathRoot.Length - 1);
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Tries to find remote virtual path provider.
        /// </summary>
        /// <param name="virtualPath">The virtual path.</param>
        /// <param name="remoteVirtualProviderInfo">The remote virtual provider info.</param>
        /// <returns></returns>
        private bool TryToFindRemoteVirtualPathProvider(string virtualPath, out RemoteVirtualPathProviderInfo remoteVirtualProviderInfo)
        {
            remoteVirtualProviderInfo = null;

            // Quick check if the path is relevant here
            if (virtualPath.StartsWith(_provider.VirtualPathRoot))
            {
                if (virtualPath == _provider.VirtualPathRoot)
                {
                    //We are in Root path
                    return false;
                }

                string[] pathInfo = virtualPath.Split(new char[] { '/' }, 4);

                if (pathInfo != null && pathInfo.Length > 2)
                {

                    Int32 pageFolderId;
                    if (Int32.TryParse(pathInfo[2], out pageFolderId) || pathInfo[2].StartsWith(_remotePageFolderVirtualPath))
                    {
                        return _remoteVirtualPathProvidersInfo.TryGetValue(_remotePageFolderVirtualPath, out remoteVirtualProviderInfo);
                    }

                    return _remoteVirtualPathProvidersInfo.TryGetValue("/" + pathInfo[2] + "/", out remoteVirtualProviderInfo);
                }
            }
            return false;
        }

        /// <summary>
        /// Supports the file resolver.
        /// </summary>
        /// <param name="virtualPath">The virtual path.</param>
        /// <returns></returns>
        public bool SupportFileResolver(string virtualPath)
        {
            RemoteVirtualPathProviderInfo remoteVirtualPathProviderInfo = null;
            TryToFindRemoteVirtualPathProvider(virtualPath, out remoteVirtualPathProviderInfo);
            return (remoteVirtualPathProviderInfo != null) && (remoteVirtualPathProviderInfo.SupportFileResolver);
        }
        #endregion

        /// <summary>
        /// Inits this instance.
        /// </summary>
        public void Init()
        {
            CreateRemoteVirtualDirectories();
        }
    }
}
